package com.microcm.system.task;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import jeasy.analysis.MMAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.microcm.common.CUtils;
import com.microcm.common.DateTimeUtils;
import com.microcm.common.Final;
import com.microcm.manage.entity.Goods;
import com.microcm.system.cache.SystemCacheKeeper;
import com.microcm.system.entity.SysMenu;
import com.microcm.system.service.SystemService;
import com.microcm.system.utils.IndexSearcherHolder;
import com.microcmframework.core.context.AppContext;
import com.microcmframework.core.job.JobRunner;

/**
 * 新建商品的索引
 * 
 * @author kansum
 */
@Lazy(false)
@Service("goodsIndexBuildJob")
public class GoodsIndexBuildJob extends JobRunner
{
	@Autowired
	private SystemService SystemService;

	@Autowired
	private SystemCacheKeeper systemCacheKeeper;

	private File indexRootDir;

	private boolean isExistSegment;

	private int cleanDiffSeconds;

	private Date startDate;

	public GoodsIndexBuildJob()
	{
		super(GoodsIndexBuildJob.class.getName(), true, "building goods index");
	}

	@Override
	protected String getCfgValue(String key)
	{
		String cfgVal = this.systemCacheKeeper.getSystemConfigValue(key, null);
		if (cfgVal == null)
		{
			cfgVal = AppContext.getInstance().getSysPropValue(key);
		}
		return cfgVal;
	}

	/**
	 * 初始化一些参数
	 */
	@Override
	protected boolean doInitialize()
	{
		// 索引目录
		this.indexRootDir = new File(AppContext.getInstance().getSysPropValue(Final.INDEX_ROOT_DIR), Final.INDEX_GOODS);
		if (!this.indexRootDir.exists())
		{
			this.indexRootDir.mkdirs();
		}
		String[] files = this.indexRootDir.list(new FilenameFilter()
		{
			@Override
			public boolean accept(File dir, String name)
			{
				return (name.endsWith(".gen") || name.endsWith(".fdt") || name.endsWith(".cfs") || name.endsWith(".cfx"));
			}
		});
		isExistSegment = files == null ? false : files.length > 0;

		// 清除索引逻辑执行的间隔秒数
		String val = CUtils.getParamFromUrl(this.getJobConfig(), "cleanDiffSeconds");
		if (CUtils.isNotEmpty(val))
			this.cleanDiffSeconds = Integer.parseInt(val);
		else
			this.cleanDiffSeconds = 3600;

		// 初始化时将当前时间记录为运行开始时间
		this.startDate = new Date();

		return super.doInitialize();
	}

	/**
	 * 开始任务运行
	 * 
	 * @throws Exception
	 */
	@Override
	public void doProcess()
	{
		Date curDate = new Date();
		long diffSecond = DateTimeUtils.getDiffSeconds(this.startDate, curDate);
		if (diffSecond > this.cleanDiffSeconds)
		{
			this.startDate = curDate;

			List<?> qryGoodsList = this.SystemService.queryReadyForCleanIndexGoods();
			if (CUtils.isEmpty(qryGoodsList))
				return;
			boolean ret = cleanGoodsIndex(qryGoodsList);
			if (ret)
			{
				this.SystemService.deleteGoodsHasCleanIndex(qryGoodsList);
			}
		}
		else
		{
			List<?> qryGoodsList = null;
			do
			{
				qryGoodsList = this.SystemService.queryReadyForBuildIndexGoods();
				if (CUtils.isEmpty(qryGoodsList))
					break;
				boolean ret = appendGoodsIndex(qryGoodsList);
				if (!ret)
					break;
				this.SystemService.updateGoodsHasIndex(qryGoodsList);
			} while (true);
		}
	}

	/**
	 * 追加商品的索引
	 * 
	 * @author kansum
	 */
	private boolean appendGoodsIndex(List<?> qryGoodsList)
	{
		Directory indexDir = null;
		try
		{
			// 先将查询索引器关掉
			IndexSearcherHolder.getInstance().closeIndexSearcher(Final.INDEX_GOODS);

			// 将庖丁封装成符合Lucene要求的Analyzer规范
			// Analyzer analyzer = new PaodingAnalyzer();
			// 使用极易分词
			Analyzer analyzer = new MMAnalyzer();
			// 接下来是标准的Lucene建立索引和检索的代码
			indexDir = FSDirectory.open(this.indexRootDir);

			IndexWriter writer = null;
			if (isExistSegment)
				writer = new IndexWriter(indexDir, analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED);
			else
				writer = new IndexWriter(indexDir, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);

			// 一条条将商品添加到索引中
			for (Object o : qryGoodsList)
			{
				Goods goods = (Goods) o;
				Integer pmnid = null;
				if (goods.getMnid() != null)
				{
					SysMenu sysMenu = systemCacheKeeper.getMenuByMnid(goods.getMnid());
					pmnid = sysMenu != null ? sysMenu.getPmnid() : null;
				}
				Document doc = new Document();
				Field field = null;
				field = new Field("numIid", CUtils.toStr(goods.getNumIid()), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				doc.add(field);
				field = new Field("title", goods.getTitle(), Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);
				doc.add(field);
				field = new Field("mnid", CUtils.toStr(goods.getMnid()), Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS);
				doc.add(field);
				// 索引父菜单ID
				if (pmnid != null)
				{
					field = new Field("pmnid", CUtils.toStr(pmnid), Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS);
					doc.add(field);
				}
				field = new Field("nick", goods.getNick(), Field.Store.YES, Field.Index.NO);
				doc.add(field);
				field = new Field("picUrl", goods.getPicUrl(), Field.Store.YES, Field.Index.NO);
				doc.add(field);
				field = new Field("price", goods.getPrice(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				doc.add(field);
				field = new Field("clickUrl", goods.getClickUrl(), Field.Store.YES, Field.Index.NO);
				doc.add(field);
				field = new Field("commissionNum", goods.getCommissionNum(), Field.Store.YES, Field.Index.NO);
				doc.add(field);
				field = new Field("shopClickUrl", goods.getShopClickUrl(), Field.Store.YES, Field.Index.NO);
				doc.add(field);
				field = new Field("sellerCreditScore", CUtils.toStr(goods.getSellerCreditScore()), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				doc.add(field);
				field = new Field("itemLocation", CUtils.toStr(goods.getItemLocation()), Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);
				doc.add(field);
				field = new Field("volume", CUtils.toStr(goods.getVolume()), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				doc.add(field);
				field = new Field("modifyTm", DateTimeUtils.convertToString(goods.getModifyTm(), DateTimeUtils.FMT_YYYYMMDDHHMMSS_SEQ), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				doc.add(field);
				if (CUtils.isNotEmpty(goods.getTags()))
				{
					field = new Field("tags", goods.getTags(), Field.Store.NO, Field.Index.ANALYZED_NO_NORMS);
					doc.add(field);
				}

				Term pkey = new Term("numIid", CUtils.toStr(goods.getNumIid()));
				writer.updateDocument(pkey, doc);
			}
			writer.optimize();
			writer.close();
			indexDir.close();

			this.logger.error("\t... " + qryGoodsList.size() + " index has been builded!");

			if (!isExistSegment)
			{
				isExistSegment = true;
			}
			return true;
		}
		catch (IOException e)
		{
			this.logger.error(e.getMessage(), e);
		}
		finally
		{
			try
			{
				if (indexDir != null && IndexWriter.isLocked(indexDir))
				{
					IndexWriter.unlock(indexDir);
				}

				// 无论如何都要开启查询索引器
				IndexSearcherHolder.getInstance().openIndexSearcher(Final.INDEX_GOODS);
			}
			catch (IOException e)
			{
				this.logger.error(e.getMessage(), e);
			}
		}
		return false;
	}

	/**
	 * 清除商品的索引
	 * 
	 * @author kansum
	 */
	private boolean cleanGoodsIndex(List<?> qryGoodsList)
	{
		Directory indexDir = null;
		try
		{
			// 先将查询索引器关掉
			IndexSearcherHolder.getInstance().closeIndexSearcher(Final.INDEX_GOODS);

			indexDir = FSDirectory.open(this.indexRootDir);

			if (!isExistSegment)
				return false;

			IndexReader reader = IndexReader.open(indexDir, false);
			// 一条条将商品从索引中删除
			for (Object o : qryGoodsList)
			{
				Goods goods = (Goods) o;

				Term pkey = new Term("numIid", CUtils.toStr(goods.getNumIid()));
				reader.deleteDocuments(pkey);
			}
			reader.close();
			indexDir.close();

			this.logger.error("\t... " + qryGoodsList.size() + " index has been removed!");

			return true;
		}
		catch (IOException e)
		{
			this.logger.error(e.getMessage(), e);
		}
		finally
		{
			try
			{
				if (indexDir != null && IndexWriter.isLocked(indexDir))
				{
					IndexWriter.unlock(indexDir);
				}

				// 无论如何都要开启查询索引器
				IndexSearcherHolder.getInstance().openIndexSearcher(Final.INDEX_GOODS);
			}
			catch (IOException e)
			{
				this.logger.error(e.getMessage(), e);
			}
		}
		return false;
	}
}
